﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.UI.DataVisualization.Charting;
using Kobets.Automation.Chimera.Common;
using Kobets.Automation.Chimera.Orchestrator.Models;
using Kobets.Automation.Chimera.Orchestrator.Shared;

namespace Kobets.Automation.Chimera.Orchestrator.Controllers
{
    [LogActionFilter]
    public class HomeController : Controller
    {
        readonly Agent[] agents = Shared.Common.GetAgentsList();

        public ActionResult Index(string group, string executionId)
        {
            ViewBag.Title = "Home";

            if (String.IsNullOrWhiteSpace(@group) && TempData["Group"] != null)
                @group = TempData["Group"].ToString();

            var groups = agents.Where(a => !String.IsNullOrWhiteSpace(a.Group)).Select(g => g.Group).Distinct().Select(o => new SelectListItem { Text = o, Value = o, Selected = (o == @group) }).ToList();

            if (String.IsNullOrWhiteSpace(group))
            {
                if (groups.Count > 0)
                {
                    groups[0].Selected = true;
                    @group = groups[0].Value;
                }
            }
            else
            {
                if (!TempData.ContainsKey("Group"))
                    TempData.Add("Group", @group);
                else
                    TempData["Group"] = @group;
            }

            ViewBag.SelectedGroup = @group;
            var exeStats = new ExecutionStatisticsEntities();
            var exeDb = new ExecutionEntities();
            var executions = (from execution in exeDb.Executions
                             orderby execution.timestamp, execution.guid
                             select execution).ToList();

            var profiles = new Dictionary<string, bool>(); // complete = true, remaining = false
            var pDb = new Models.ProfilesEntities();
            var lastExecutionId = Guid.Empty;
            var exeGr = executions.Where(g => g.group == @group).ToList();

            if (exeGr.Count > 0)
                lastExecutionId = executions.Where(g => g.group == @group).OrderBy(o => o.timestamp).Last().guid;

            if (!String.IsNullOrWhiteSpace(group) && executions.Count > 0)
            {
                var lastGuid = Guid.Empty;
                var exeList = GetExecutionsList();

                if (exeList.Count > 0)
                    lastGuid = exeList.Last().Key;

                if (!String.IsNullOrWhiteSpace(executionId))
                    lastGuid = Guid.Parse(executionId);

                if (lastGuid != Guid.Empty)
                {
                    ViewBag.ExecutionList = exeList.Reverse().Select(kvp => new SelectListItem { Text = kvp.Value, Value = kvp.Key.ToString(), Selected = (kvp.Key == lastGuid)}).Take(10).ToList();
                    var stat = CommonMethods.GetExecutionStatistic(agents.ToList(), lastGuid);
                    ViewBag.PDate = stat.Date;
                    ViewBag.PId = stat.Id;
                    ViewBag.PRuntimes = stat.Runtimes;
                    ViewBag.PGroup = stat.Group;
                    ViewBag.PStatus = stat.Status;
                }
            }
            else
            {
                ViewBag.PreviosRunDate = "N/A";
                ViewBag.PreviosRunId = 0;
            }

            ViewBag.Group = groups;

            var groupStatus = "Idle";

            if (agents.Where(g => g.Group == @group).Any(agent => agent.Status == Enums.AgentStatus.Running))
                groupStatus = "Running";

            ViewBag.CurrentState = groupStatus;
            
            if (groupStatus == "Running" && lastExecutionId != Guid.Empty)
            {
                var remTime = 0;
                var currentExecution = exeDb.Executions.ToList().Where(g => g.@group == @group && g.guid == lastExecutionId).ToList();
                var dupMode = false;

                foreach (var execution in currentExecution)
                {
                    var tcArr = new List<string>();
                    
                    if (execution.testcase.Contains("|"))
                    {
                        tcArr = execution.testcase.Split('|').ToList();
                        dupMode = true;
                    }
                    else
                        tcArr.Add(execution.testcase);

                    foreach (var tc in tcArr)
                    {
                        if (execution.status != "Complete")
                        {
                            if (execution.status == "Pending")
                                if (!profiles.ContainsKey(tc))
                                    profiles.Add(tc, false);

                            var es = exeStats.ExecutionStatistics.Where(w => w.profileName == tc && w.runtime > 25 && w.runtime < 300).ToList();
                            
                            if (es.Count > 0)
                            {
                                remTime += (int)es.Select(stat => stat.runtime).Average();
                            }
                            else
                            {
                                var pt = pDb.Profiles.ToList().FirstOrDefault(p => p.ProfileName == tc);

                                if (pt != null && pt.Weight.HasValue)
                                    remTime += pt.Weight.Value;
                            }
                        }
                        else
                            if (!profiles.ContainsKey(tc))
                                profiles.Add(tc, true);
                    }

                    if (dupMode) break;
                }

                int hours;
                int minutes;
                
                if (currentExecution.Count > 0 && currentExecution.First().testcase.Contains("|"))
                    hours = Math.DivRem(remTime, 60, out minutes);
                else
                    hours = Math.DivRem(remTime / agents.Count(a => a.Group == @group && a.Status == Enums.AgentStatus.Running), 60, out minutes);

                ViewBag.RemainingTime = String.Format("+{0}h +{1}m", hours, minutes);
            }

            return View(profiles);
        }

        public ActionResult GetCurrentState(string group)
        {
            if (String.IsNullOrWhiteSpace(@group) && TempData["Group"] != null)
                @group = TempData["Group"].ToString();

            if (String.IsNullOrWhiteSpace(group))
            {
                var agents = Shared.Common.GetAgentsList();
                var groups = agents.Where(a => !String.IsNullOrWhiteSpace(a.Group)).Select(g => g.Group).Distinct().Select(o => new { Group = o }).ToList();
                group = groups[0].Group;
            }
            else
            {
                if (!TempData.ContainsKey("Group"))
                    TempData.Add("Group", @group);
                else
                    TempData["Group"] = @group;
            }

            var exeDb = new ExecutionEntities();
            var lastExecutionId = exeDb.Executions.ToList().Where(w => w.@group == @group).OrderBy(o => o.runId).Last().guid;
            var profiles = exeDb.Executions.ToList().Where(g => g.@group == @group && g.guid == lastExecutionId).Where(execution => execution.status == "Running").Distinct(new ExecutionComparer()).Aggregate("", (current, execution) => current + (", " + execution.testcase.Replace("|", ", ")));
            profiles = profiles.Length > 0 ? profiles.Remove(0, 2) : "";
            
            return Content(profiles);
        }

        public FileResult CreateChart()
        {
            var ms = new MemoryStream();
            CreateChart(SeriesChartType.RangeColumn, GetChartData).SaveImage(ms);
            return File(ms.GetBuffer(), @"image/png");
        }

        public ActionResult StopExecution(string g)
        {
            if (!String.IsNullOrWhiteSpace(g))
            {
                Shared.Common.StopExecution(g);
            }

            return RedirectToAction("Index");
        }

        #region Private Methods

        private Dictionary<Guid, string> GetExecutionsList()
        {
            var exeDb = new ExecutionEntities();
            var executions = (from execution in exeDb.Executions
                              orderby execution.timestamp, execution.guid
                              select execution).ToList();
            var guids = executions.Where(w => w.status == "Complete").OrderBy(o => o.timestamp).Select(s => s.guid).Distinct();
            var retVal = new Dictionary<Guid, string>();

            foreach (var guid in guids.Where(guid => executions.Where(w => w.guid == guid).All(a => a.status == "Complete")))
            {
                var gu = guid;
                var exe = executions.First(g => g.guid == gu);
                retVal.Add(exe.guid, new StringBuilder("Group: " + exe.group + " at " + exe.timestamp).ToString());
            }

            return retVal;
        }

        private IList<ChartData> GetChartData()
        {
            var db = new AuditEntities();
            var data = db.GetChartDataLastMonth();

            return data.Select(eChartData => new ChartData(eChartData.Date, eChartData.Amount)).ToList();
        }

        private Chart CreateChart(SeriesChartType chartType, Func<IList<ChartData>> data)
        {
            var chart = new Chart
                            {
                                Width = 400,
                                Height = 300,
                                BackColor = Color.FromArgb(211, 223, 240),
                                BorderlineDashStyle = ChartDashStyle.Solid,
                                BackSecondaryColor = Color.White,
                                BackGradientStyle = GradientStyle.TopBottom,
                                BorderlineWidth = 1,
                                Palette = ChartColorPalette.BrightPastel,
                                BorderlineColor = Color.FromArgb(26, 59, 105),
                                RenderType = RenderType.BinaryStreaming,
                                BorderSkin = {SkinStyle = BorderSkinStyle.Emboss},
                                AntiAliasing = AntiAliasingStyles.All,
                                TextAntiAliasingQuality = TextAntiAliasingQuality.Normal
                            };

            chart.Titles.Add(CreateTitle());
            chart.Series.Add(CreateSeries(data, chartType));
            chart.ChartAreas.Add(CreateChartArea());

            return chart;
        }

        private Title CreateTitle()
        {
            var title = new Title
                            {
                                Text = "Execution History",
                                ShadowColor = Color.FromArgb(32, 0, 0, 0),
                                Font = new Font("Trebuchet MS", 14F, FontStyle.Bold),
                                ShadowOffset = 3,
                                ForeColor = Color.FromArgb(26, 59, 105)
                            };

            return title;
        }

        private Legend CreateLegend()
        {
            var legend = new Legend
                             {
                                 Enabled = true,
                                 ShadowColor = Color.FromArgb(32, 0, 0, 0),
                                 Font = new Font("Trebuchet MS", 14F, FontStyle.Bold),
                                 ShadowOffset = 3,
                                 ForeColor = Color.FromArgb(26, 59, 105),
                                 Title = "Legend"
                             };

            return legend;
        }

        private Series CreateSeries(Func<IList<ChartData>> data, SeriesChartType chartType)
        {
            var seriesDetail = new Series
                                   {
                                       Name = "Execution History",
                                       IsValueShownAsLabel = false,
                                       Color = Color.FromArgb(198, 99, 99),
                                       ChartType = chartType,
                                       BorderWidth = 2,
                                       IsXValueIndexed = true,
                                       XValueType = ChartValueType.Date
                                   };
            
            foreach (var chartData in data.Invoke())
                seriesDetail.Points.AddXY(chartData.Date, chartData.Amount);

            seriesDetail.IsValueShownAsLabel = true;
            seriesDetail.ChartArea = "Execution History";

            return seriesDetail;
        }

        private ChartArea CreateChartArea()
        {
            var chartArea = new ChartArea
                                {
                                    Name = "Execution History",
                                    BackColor = Color.Transparent,
                                    AxisX = {IsLabelAutoFit = true},
                                    AxisY = {IsLabelAutoFit = true, LabelStyle = {Enabled = false}}
                                };

            chartArea.AxisX.LabelStyle.Font =
               new Font("Verdana,Arial,Helvetica,sans-serif",
                        8F, FontStyle.Regular);
            chartArea.AxisY.LabelStyle.Font =
               new Font("Verdana,Arial,Helvetica,sans-serif",
                        8F, FontStyle.Regular);
            chartArea.AxisY.LineColor = Color.FromArgb(64, 64, 64, 64);
            chartArea.AxisX.LineColor = Color.FromArgb(64, 64, 64, 64);
            chartArea.AxisY.MajorGrid.LineColor = Color.FromArgb(64, 64, 64, 64);
            chartArea.AxisX.MajorGrid.LineColor = Color.FromArgb(64, 64, 64, 64);

            return chartArea;
        }

        #endregion
    }
}
